<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Blockly Playground</title>

    <script type="module">
      import {COMPRESSED, loadScript} from './scripts/load.mjs';

      import * as Blockly from '../build/blockly.loader.mjs';
      import '../build/blocks.loader.mjs';
      import {dartGenerator} from '../build/dart.loader.mjs';
      import {luaGenerator} from '../build/lua.loader.mjs';
      import {javascriptGenerator} from '../build/javascript.loader.mjs';
      import {phpGenerator} from '../build/php.loader.mjs';
      import {pythonGenerator} from '../build/python.loader.mjs';

      await loadScript('../build/msg/en.js');
      await loadScript('playgrounds/screenshot.js');
      await loadScript('../node_modules/@blockly/dev-tools/dist/index.js');

      var workspace = null;

      function start() {
        setBackgroundColour();

        // Parse the URL arguments.
        var match = location.search.match(/dir=([^&]+)/);
        var rtl = match && match[1] == 'rtl';
        document.forms.options.elements.dir.selectedIndex = Number(rtl);
        var toolbox = getToolboxElement();
        setToolboxDropdown();
        match = location.search.match(/side=([^&]+)/);
        var autoimport = !!location.search.match(/autoimport=([^&]+)/);
        // Create main workspace.
        workspace = Blockly.inject('blocklyDiv', {
          comments: true,
          collapse: true,
          disable: true,
          grid: {
            spacing: 25,
            length: 3,
            colour: '#ccc',
            snap: true,
          },
          horizontalLayout: false,
          maxBlocks: Infinity,
          maxInstances: {'test_basic_limit_instances': 3},
          maxTrashcanContents: 256,
          media: '../media/',
          oneBasedIndex: true,
          readOnly: false,
          rtl: rtl,
          move: {
            scrollbars: true,
            drag: true,
            wheel: false,
          },
          toolbox: toolbox,
          toolboxPosition: 'start',
          renderer: 'geras',
          zoom: {
            controls: true,
            wheel: true,
            startScale: 1.0,
            maxScale: 4,
            minScale: 0.25,
            scaleSpeed: 1.1,
          },
        });
        initToolbox(workspace);

        workspace.configureContextMenu = configureContextMenu;
        Blockly.ContextMenuItems.registerCommentOptions();

        // Restore previously displayed text.
        if (sessionStorage) {
          var text = sessionStorage.getItem('textarea');
          if (text) {
            document.getElementById('importExport').value = text;
          }
          // Restore event logging state.
          var logMainEventsState = sessionStorage.getItem('logEvents');
          logEvents(Boolean(Number(logMainEventsState)));
          var logToolboxFlyoutEventsState =
            sessionStorage.getItem('logFlyoutEvents');
          logFlyoutEvents(Boolean(Number(logToolboxFlyoutEventsState)));
        } else {
          // MSIE 11 does not support sessionStorage on file:// URLs.
          logEvents(false);
        }
        taChange();
        if (autoimport) {
          load();
        }

        addEventHandlers();
      }

      /**
       * Set background colour to differentiate between compressed and
       * uncompressed mode.
       */
      function setBackgroundColour() {
        if (!COMPRESSED) {
          document.body.style.backgroundColor = '#d6d6ff'; // Familiar lilac.
        } else {
          document.body.style.backgroundColor = '#60fcfc'; // Unfamiliar blue.
        }
      }

      function getToolboxSuffix() {
        var match = location.search.match(/toolbox=([^&]+)/);
        // Default to the basic toolbox with categories and untyped variables,
        // but override that if the toolbox type is set in the URL.
        return match ? match[1] : 'categories';
      }

      function getToolboxElement() {
        var toolboxSuffix = getToolboxSuffix();
        if (toolboxSuffix == 'test-blocks') {
          if (typeof window.toolboxTestBlocks !== 'undefined') {
            return toolboxTestBlocks;
          } else {
            alert(
              "You need to run 'npm install' in order to use the test blocks.",
            );
            toolboxSuffix = 'categories';
          }
        }
        // The three possible values are: "simple", "categories",
        // "categories-typed-variables".
        return document.getElementById('toolbox-' + toolboxSuffix);
      }

      function setToolboxDropdown() {
        var toolboxNames = [
          'toolbox-categories',
          'toolbox-categories-typed-variables',
          'toolbox-simple',
          'toolbox-test-blocks',
        ];
        var toolboxSuffix = getToolboxSuffix();
        document.forms.options.elements.toolbox.selectedIndex =
          toolboxNames.indexOf('toolbox-' + toolboxSuffix);
      }

      function initToolbox(workspace) {
        var toolboxSuffix = getToolboxSuffix();
        if (
          toolboxSuffix == 'test-blocks' &&
          typeof window.toolboxTestBlocksInit !== 'undefined'
        ) {
          toolboxTestBlocksInit(workspace);
        }
      }

      function saveXml() {
        var output = document.getElementById('importExport');
        var xml = Blockly.Xml.workspaceToDom(workspace);
        output.value = Blockly.Xml.domToPrettyText(xml);
        output.focus();
        output.select();
        taChange();
      }

      function saveJson() {
        var output = document.getElementById('importExport');
        var state = Blockly.serialization.workspaces.save(workspace);
        output.value = JSON.stringify(state, null, 2);
        output.focus();
        output.select();
        taChange();
      }

      function load() {
        var input = document.getElementById('importExport');
        if (!input.value) {
          return;
        }
        var valid = saveIsValid(input.value);
        if (valid.json) {
          var state = JSON.parse(input.value);
          Blockly.serialization.workspaces.load(state, workspace);
        } else if (valid.xml) {
          var xml = Blockly.utils.xml.textToDom(input.value);
          Blockly.Xml.domToWorkspace(xml, workspace);
        }
        taChange();
      }

      function toCode(lang) {
        var generator = {
          'JavaScript': javascriptGenerator,
          'Python': pythonGenerator,
          'PHP': phpGenerator,
          'Lua': luaGenerator,
          'Dart': dartGenerator,
        }[lang];
        var output = document.getElementById('importExport');
        output.value = generator.workspaceToCode(workspace);
        taChange();
      }

      // Disable the "Load" button if the save state is invalid.
      // Preserve text between page reloads.
      function taChange() {
        var textarea = document.getElementById('importExport');
        if (sessionStorage) {
          sessionStorage.setItem('textarea', textarea.value);
        }
        var valid = saveIsValid(textarea.value);
        document.getElementById('import').disabled = !valid.json && !valid.xml;
      }

      function saveIsValid(save) {
        var validJson = true;
        try {
          JSON.parse(save);
        } catch (e) {
          validJson = false;
        }
        var validXml = true;
        try {
          Blockly.utils.xml.textToDom(save);
        } catch (e) {
          validXml = false;
        }
        return {
          json: validJson,
          xml: validXml,
        };
      }

      function logEvents(state) {
        var checkbox = document.getElementById('logCheck');
        checkbox.checked = state;
        if (sessionStorage) {
          sessionStorage.setItem('logEvents', Number(state));
        }
        if (state) {
          workspace.addChangeListener(logger);
        } else {
          workspace.removeChangeListener(logger);
        }
      }

      function logFlyoutEvents(state) {
        var checkbox = document.getElementById('logFlyoutCheck');
        checkbox.checked = state;
        if (sessionStorage) {
          sessionStorage.setItem('logFlyoutEvents', Number(state));
        }
        var flyoutWorkspace = workspace.getFlyout()?.getWorkspace();
        if (!flyoutWorkspace) return;
        if (state) {
          flyoutWorkspace.addChangeListener(logger);
        } else {
          flyoutWorkspace.removeChangeListener(logger);
        }
      }

      function configureContextMenu(menuOptions, e) {
        var screenshotOption = {
          text: 'Download Screenshot',
          enabled: workspace.getTopBlocks().length,
          callback: function () {
            downloadScreenshot(workspace);
          },
        };
        menuOptions.push(screenshotOption);
      }

      function logger(e) {
        console.log(e);
      }

      function scatter(n) {
        var prototypes = [];
        var toolbox = getToolboxElement();
        var blocks = toolbox.getElementsByTagName('block');
        for (var i = 0, block; (block = blocks[i]); i++) {
          prototypes.push(block.getAttribute('type'));
        }
        for (var i = 0; i < n; i++) {
          var prototype =
            prototypes[Math.floor(Math.random() * prototypes.length)];
          var block = workspace.newBlock(prototype);
          block.initSvg();
          block
            .getSvgRoot()
            .setAttribute(
              'transform',
              'translate(' +
                Math.round(Math.random() * 450 + 40) +
                ', ' +
                Math.round(Math.random() * 600 + 40) +
                ')',
            );
          block.render();
        }
      }

      function spaghetti(n) {
        var xml = spaghettiXml;
        for (var i = 0; i < n; i++) {
          xml = xml.replace(
            /(<(statement|next)( name="DO0")?>)<\//g,
            '$1' + spaghettiXml + '</',
          );
        }
        xml =
          '<xml xmlns="https://developers.google.com/blockly/xml">' +
          xml +
          '</xml>';
        var dom = Blockly.utils.xml.textToDom(xml);
        console.time('Spaghetti domToWorkspace');
        Blockly.Xml.domToWorkspace(dom, workspace);
        console.timeEnd('Spaghetti domToWorkspace');
      }
      var spaghettiXml = [
        '  <block type="controls_if">',
        '    <value name="IF0">',
        '      <block type="logic_compare">',
        '        <field name="OP">EQ</field>',
        '        <value name="A">',
        '          <block type="math_arithmetic">',
        '            <field name="OP">MULTIPLY</field>',
        '            <value name="A">',
        '              <block type="math_number">',
        '                <field name="NUM">6</field>',
        '              </block>',
        '            </value>',
        '            <value name="B">',
        '              <block type="math_number">',
        '                <field name="NUM">7</field>',
        '              </block>',
        '            </value>',
        '          </block>',
        '        </value>',
        '        <value name="B">',
        '          <block type="math_number">',
        '            <field name="NUM">42</field>',
        '          </block>',
        '        </value>',
        '      </block>',
        '    </value>',
        '    <statement name="DO0"></statement>',
        '    <next></next>',
        '  </block>',
      ].join('\n');

      function jsoSpaghetti(n) {
        var str = spaghettiJs;
        for (var i = 0; i < n; i++) {
          str = str.replace(/{}/g, `{"block":${spaghettiJs}}`);
        }
        var obj = {
          'blocks': {
            'blocks': [JSON.parse(str)],
          },
        };
        console.time('Spaghetti serialization');
        Blockly.serialization.workspaces.load(obj, workspace);
        console.timeEnd('Spaghetti serialization');
      }
      var spaghettiJs = JSON.stringify({
        'type': 'controls_if',
        'inputs': {
          'IF0': {
            'block': {
              'type': 'logic_compare',
              'fields': {
                'OP': 'EQ',
              },
              'inputs': {
                'A': {
                  'block': {
                    'type': 'math_arithmetic',
                    'fields': {
                      'OP': 'MULTIPLY',
                    },
                    'inputs': {
                      'A': {
                        'block': {
                          'type': 'math_number',
                          'fields': {
                            'NUM': 6,
                          },
                        },
                      },
                      'B': {
                        'block': {
                          'type': 'math_number',
                          'fields': {
                            'NUM': 7,
                          },
                        },
                      },
                    },
                  },
                },
                'B': {
                  'block': {
                    'type': 'math_number',
                    'fields': {
                      'NUM': 42,
                    },
                  },
                },
              },
            },
          },
          'DO0': {},
        },
        'next': {},
      });

      function addEventHandlers() {
        document
          .getElementById('save-json')
          .addEventListener('click', saveJson);
        document.getElementById('save-xml').addEventListener('click', saveXml);
        document.getElementById('import').addEventListener('click', load);

        document
          .getElementById('to-code-js')
          .addEventListener('click', () => toCode('JavaScript'));
        document
          .getElementById('to-code-py')
          .addEventListener('click', () => toCode('Python'));
        document
          .getElementById('to-code-php')
          .addEventListener('click', () => toCode('PHP'));
        document
          .getElementById('to-code-lua')
          .addEventListener('click', () => toCode('Lua'));
        document
          .getElementById('to-code-dart')
          .addEventListener('click', () => toCode('Dart'));

        document
          .getElementById('scatter')
          .addEventListener('click', () => scatter(100));
        document
          .getElementById('spaghetti-xml')
          .addEventListener('click', () => spaghetti(8));
        document
          .getElementById('spaghetti-js')
          .addEventListener('click', () => jsoSpaghetti(8));

        document
          .getElementById('logCheck')
          .addEventListener('click', function () {
            logEvents(this.checked);
          });
        document
          .getElementById('logFlyoutCheck')
          .addEventListener('click', function () {
            logFlyoutEvents(this.checked);
          });

        document
          .getElementById('importExport')
          .addEventListener('change', taChange);
        document
          .getElementById('importExport')
          .addEventListener('keyup', taChange);

        document.getElementById('show').addEventListener('click', function () {
          workspace.setVisible(true);
        });
        document.getElementById('hide').addEventListener('click', function () {
          workspace.setVisible(false);
        });
      }

      // Call start().  Because this <script> has type=module, it is
      // automatically deferred, so it will not be run until after the
      // document has been parsed, but before firing DOMContentLoaded.
      start();
    </script>

    <style>
      html,
      body {
        height: 100%;
      }
      body {
        background-color: #fff;
        font-family: sans-serif;
        overflow: hidden;
      }
      h1 {
        font-weight: normal;
        font-size: 140%;
      }
      #blocklyDiv {
        float: right;
        height: 95%;
        width: 70%;
      }
      #importExport {
        font-family: monospace;
      }

      .ioLabel > .blocklyFlyoutLabelText {
        font-style: italic;
      }

      #blocklyDiv.renderingDebug .blockRenderDebug {
        display: block;
      }

      .playgroundToggleOptions {
        list-style: none;
        padding: 0;
      }
      .playgroundToggleOptions li {
        margin-top: 1em;
      }

      .zelos-renderer .blocklyFlyoutButton .blocklyText {
        font-size: 1.5rem;
      }
    </style>
  </head>
  <body>
    <div id="blocklyDiv"></div>

    <h1>Blockly Playground</h1>

    <p>
      <input id="show" type="button" value="Show" /> -
      <input id="hide" type="button" value="Hide" /> -
      <a href="playgrounds/advanced_playground.html">Advanced</a>
    </p>

    <form id="options">
      <select name="dir" onchange="document.forms.options.submit()">
        <option value="ltr">LTR</option>
        <option value="rtl">RTL</option>
      </select>
      <select name="toolbox" onchange="document.forms.options.submit()">
        <option value="categories">Categories (untyped variables)</option>
        <option value="categories-typed-variables">
          Categories (typed variables)
        </option>
        <option value="simple">Simple</option>
        <option value="test-blocks">Test Blocks</option>
      </select>
    </form>
    <p>
      <input id="save-json" type="button" value="Save JSON" />
      <input id="save-xml" type="button" value="Save XML" />
      <input type="button" value="Load" id="import" />
      <br />
      <input id="to-code-js" type="button" value="To JavaScript" />
      <input id="to-code-py" type="button" value="To Python" />
      <input id="to-code-php" type="button" value="To PHP" />
      <input id="to-code-lua" type="button" value="To Lua" />
      <input id="to-code-dart" type="button" value="To Dart" />
      <br />
      <textarea id="importExport" style="width: 26%; height: 12em"></textarea>
    </p>

    <p>
      Stress test: &nbsp;
      <input id="scatter" type="button" value="Scatter!" />
      <input id="spaghetti-xml" type="button" value="Spaghetti!" />
      <input id="spaghetti-js" type="button" value="JS Spaghetti!" />
    </p>
    <ul class="playgroundToggleOptions">
      <li>
        <label for="logCheck">Log main workspace events:</label>
        <input type="checkbox" id="logCheck" />
      </li>
      <li>
        <label for="logFlyoutCheck">Log flyout events:</label>
        <input type="checkbox" id="logFlyoutCheck" />
      </li>
    </ul>

    <!-- The next three blocks of XML are sample toolboxes for testing basic
  configurations.  For more information on building toolboxes, see https://developers.google.com/blockly/guides/configure/web/toolbox -->

    <!-- toolbox-simple is an always-open flyout with no category menu.
  Always-open flyouts are a good idea if you have a small number of blocks. -->
    <xml
      xmlns="https://developers.google.com/blockly/xml"
      id="toolbox-simple"
      style="display: none">
      <block type="controls_ifelse"></block>
      <block type="logic_compare"></block>
      <!-- <block type="control_repeat"></block> -->
      <block type="logic_operation"></block>
      <block type="controls_repeat_ext">
        <value name="TIMES">
          <shadow type="math_number">
            <field name="NUM">10</field>
          </shadow>
        </value>
      </block>
      <block type="logic_operation"></block>
      <block type="logic_negate"></block>
      <block type="logic_boolean"></block>
      <block type="logic_null" disabled="true"></block>
      <block type="logic_ternary"></block>
      <block type="text_charAt">
        <value name="VALUE">
          <block type="variables_get">
            <field name="VAR">text</field>
          </block>
        </value>
      </block>
    </xml>

    <!-- toolbox-categories has a category menu and an auto-closing flyout.  The
  Variables category uses untyped variable blocks.
  See https://developers.google.com/blockly/guides/create-custom-blocks/variables#untyped_variable_blocks for more information. -->
    <xml
      xmlns="https://developers.google.com/blockly/xml"
      id="toolbox-categories"
      style="display: none">
      <category name="Logic" categorystyle="logic_category">
        <block type="controls_if"></block>
        <block type="logic_compare"></block>
        <block type="logic_operation"></block>
        <block type="logic_negate"></block>
        <block type="logic_boolean"></block>
        <block type="logic_null" disabled="true"></block>
        <block type="logic_ternary"></block>
      </category>
      <category name="Loops" categorystyle="loop_category">
        <block type="controls_repeat_ext">
          <value name="TIMES">
            <shadow type="math_number">
              <field name="NUM">10</field>
            </shadow>
          </value>
        </block>
        <block type="controls_repeat" disabled="true"></block>
        <block type="controls_whileUntil"></block>
        <block type="controls_for">
          <value name="FROM">
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
          <value name="TO">
            <shadow type="math_number">
              <field name="NUM">10</field>
            </shadow>
          </value>
          <value name="BY">
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
        </block>
        <block type="controls_forEach"></block>
        <block type="controls_flow_statements"></block>
      </category>
      <category name="Math" categorystyle="math_category">
        <block type="math_number" gap="32">
          <field name="NUM">123</field>
        </block>
        <block type="math_arithmetic">
          <value name="A">
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
          <value name="B">
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
        </block>
        <block type="math_single">
          <value name="NUM">
            <shadow type="math_number">
              <field name="NUM">9</field>
            </shadow>
          </value>
        </block>
        <block type="math_trig">
          <value name="NUM">
            <shadow type="math_number">
              <field name="NUM">45</field>
            </shadow>
          </value>
        </block>
        <block type="math_constant"></block>
        <block type="math_number_property">
          <value name="NUMBER_TO_CHECK">
            <shadow type="math_number">
              <field name="NUM">0</field>
            </shadow>
          </value>
        </block>
        <block type="math_round">
          <value name="NUM">
            <shadow type="math_number">
              <field name="NUM">3.1</field>
            </shadow>
          </value>
        </block>
        <block type="math_on_list"></block>
        <block type="math_modulo">
          <value name="DIVIDEND">
            <shadow type="math_number">
              <field name="NUM">64</field>
            </shadow>
          </value>
          <value name="DIVISOR">
            <shadow type="math_number">
              <field name="NUM">10</field>
            </shadow>
          </value>
        </block>
        <block type="math_constrain">
          <value name="VALUE">
            <shadow type="math_number">
              <field name="NUM">50</field>
            </shadow>
          </value>
          <value name="LOW">
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
          <value name="HIGH">
            <shadow type="math_number">
              <field name="NUM">100</field>
            </shadow>
          </value>
        </block>
        <block type="math_random_int">
          <value name="FROM">
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
          <value name="TO">
            <shadow type="math_number">
              <field name="NUM">100</field>
            </shadow>
          </value>
        </block>
        <block type="math_random_float"></block>
        <block type="math_atan2">
          <value name="X">
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
          <value name="Y">
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
        </block>
      </category>
      <category name="Text" categorystyle="text_category">
        <block type="text"></block>
        <block type="text_join"></block>
        <block type="text_append">
          <value name="TEXT">
            <shadow type="text"></shadow>
          </value>
        </block>
        <block type="text_length">
          <value name="VALUE">
            <shadow type="text">
              <field name="TEXT">abc</field>
            </shadow>
          </value>
        </block>
        <block type="text_isEmpty">
          <value name="VALUE">
            <shadow type="text">
              <field name="TEXT"></field>
            </shadow>
          </value>
        </block>
        <block type="text_indexOf">
          <value name="VALUE">
            <block type="variables_get">
              <field name="VAR">text</field>
            </block>
          </value>
          <value name="FIND">
            <shadow type="text">
              <field name="TEXT">abc</field>
            </shadow>
          </value>
        </block>
        <block type="text_charAt">
          <value name="VALUE">
            <block type="variables_get">
              <field name="VAR">text</field>
            </block>
          </value>
        </block>
        <block type="text_getSubstring">
          <value name="STRING">
            <block type="variables_get">
              <field name="VAR">text</field>
            </block>
          </value>
        </block>
        <block type="text_changeCase">
          <value name="TEXT">
            <shadow type="text">
              <field name="TEXT">abc</field>
            </shadow>
          </value>
        </block>
        <block type="text_trim">
          <value name="TEXT">
            <shadow type="text">
              <field name="TEXT">abc</field>
            </shadow>
          </value>
        </block>
        <block type="text_count">
          <value name="SUB">
            <shadow type="text"></shadow>
          </value>
          <value name="TEXT">
            <shadow type="text"></shadow>
          </value>
        </block>
        <block type="text_replace">
          <value name="FROM">
            <shadow type="text"></shadow>
          </value>
          <value name="TO">
            <shadow type="text"></shadow>
          </value>
          <value name="TEXT">
            <shadow type="text"></shadow>
          </value>
        </block>
        <block type="text_reverse">
          <value name="TEXT">
            <shadow type="text"></shadow>
          </value>
        </block>
        <label text="Input/Output:" web-class="ioLabel"></label>
        <block type="text_print">
          <value name="TEXT">
            <shadow type="text">
              <field name="TEXT">abc</field>
            </shadow>
          </value>
        </block>
        <block type="text_prompt_ext">
          <value name="TEXT">
            <shadow type="text">
              <field name="TEXT">abc</field>
            </shadow>
          </value>
        </block>
      </category>
      <category name="Lists" categorystyle="list_category">
        <block type="lists_create_with">
          <mutation items="0"></mutation>
        </block>
        <block type="lists_create_with"></block>
        <block type="lists_repeat">
          <value name="NUM">
            <shadow type="math_number">
              <field name="NUM">5</field>
            </shadow>
          </value>
        </block>
        <block type="lists_length"></block>
        <block type="lists_isEmpty"></block>
        <block type="lists_indexOf">
          <value name="VALUE">
            <block type="variables_get">
              <field name="VAR">list</field>
            </block>
          </value>
        </block>
        <block type="lists_getIndex">
          <value name="VALUE">
            <block type="variables_get">
              <field name="VAR">list</field>
            </block>
          </value>
        </block>
        <block type="lists_setIndex">
          <value name="LIST">
            <block type="variables_get">
              <field name="VAR">list</field>
            </block>
          </value>
        </block>
        <block type="lists_getSublist">
          <value name="LIST">
            <block type="variables_get">
              <field name="VAR">list</field>
            </block>
          </value>
        </block>
        <block type="lists_split">
          <value name="DELIM">
            <shadow type="text">
              <field name="TEXT">,</field>
            </shadow>
          </value>
        </block>
        <block type="lists_sort"></block>
        <block type="lists_reverse"></block>
      </category>
      <sep></sep>
      <category
        name="Variables"
        categorystyle="variable_category"
        custom="VARIABLE"></category>
      <category
        name="Functions"
        categorystyle="procedure_category"
        custom="PROCEDURE"></category>
    </xml>

    <!-- toolbox-categories-typed-variables has a category menu and an
  auto-closing flyout.  The Variables category uses typed variable blocks.
  See https://developers.google.com/blockly/guides/create-custom-blocks/variables#typed_variable_blocks for more information. -->
    <xml
      xmlns="https://developers.google.com/blockly/xml"
      id="toolbox-categories-typed-variables"
      style="display: none">
      <category name="Logic" categorystyle="logic_category">
        <block type="controls_if"></block>
        <block type="logic_compare"></block>
        <block type="logic_operation"></block>
        <block type="logic_negate"></block>
        <block type="logic_boolean"></block>
        <block type="logic_null" disabled="true"></block>
        <block type="logic_ternary"></block>
      </category>
      <category name="Loops" categorystyle="loop_category">
        <block type="controls_repeat_ext">
          <value name="TIMES">
            <shadow type="math_number">
              <field name="NUM">10</field>
            </shadow>
          </value>
        </block>
        <block type="controls_repeat" disabled="true"></block>
        <block type="controls_whileUntil"></block>
        <block type="controls_for">
          <value name="FROM">
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
          <value name="TO">
            <shadow type="math_number">
              <field name="NUM">10</field>
            </shadow>
          </value>
          <value name="BY">
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
        </block>
        <block type="controls_forEach"></block>
        <block type="controls_flow_statements"></block>
      </category>
      <category name="Math" categorystyle="math_category">
        <block type="math_number" gap="32">
          <field name="NUM">123</field>
        </block>
        <block type="math_arithmetic">
          <value name="A">
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
          <value name="B">
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
        </block>
        <block type="math_single">
          <value name="NUM">
            <shadow type="math_number">
              <field name="NUM">9</field>
            </shadow>
          </value>
        </block>
        <block type="math_trig">
          <value name="NUM">
            <shadow type="math_number">
              <field name="NUM">45</field>
            </shadow>
          </value>
        </block>
        <block type="math_constant"></block>
        <block type="math_number_property">
          <value name="NUMBER_TO_CHECK">
            <shadow type="math_number">
              <field name="NUM">0</field>
            </shadow>
          </value>
        </block>
        <block type="math_round">
          <value name="NUM">
            <shadow type="math_number">
              <field name="NUM">3.1</field>
            </shadow>
          </value>
        </block>
        <block type="math_on_list"></block>
        <block type="math_modulo">
          <value name="DIVIDEND">
            <shadow type="math_number">
              <field name="NUM">64</field>
            </shadow>
          </value>
          <value name="DIVISOR">
            <shadow type="math_number">
              <field name="NUM">10</field>
            </shadow>
          </value>
        </block>
        <block type="math_constrain">
          <value name="VALUE">
            <shadow type="math_number">
              <field name="NUM">50</field>
            </shadow>
          </value>
          <value name="LOW">
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
          <value name="HIGH">
            <shadow type="math_number">
              <field name="NUM">100</field>
            </shadow>
          </value>
        </block>
        <block type="math_random_int">
          <value name="FROM">
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
          <value name="TO">
            <shadow type="math_number">
              <field name="NUM">100</field>
            </shadow>
          </value>
        </block>
        <block type="math_random_float"></block>
        <block type="math_atan2">
          <value name="X">
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
          <value name="Y">
            <shadow type="math_number">
              <field name="NUM">1</field>
            </shadow>
          </value>
        </block>
      </category>
      <category name="Text" categorystyle="text_category">
        <block type="text"></block>
        <block type="text_join"></block>
        <block type="text_append">
          <value name="TEXT">
            <shadow type="text"></shadow>
          </value>
        </block>
        <block type="text_length">
          <value name="VALUE">
            <shadow type="text">
              <field name="TEXT">abc</field>
            </shadow>
          </value>
        </block>
        <block type="text_isEmpty">
          <value name="VALUE">
            <shadow type="text">
              <field name="TEXT"></field>
            </shadow>
          </value>
        </block>
        <block type="text_indexOf">
          <value name="VALUE">
            <block type="variables_get">
              <field name="VAR">text</field>
            </block>
          </value>
          <value name="FIND">
            <shadow type="text">
              <field name="TEXT">abc</field>
            </shadow>
          </value>
        </block>
        <block type="text_charAt">
          <value name="VALUE">
            <block type="variables_get">
              <field name="VAR">text</field>
            </block>
          </value>
        </block>
        <block type="text_getSubstring">
          <value name="STRING">
            <block type="variables_get">
              <field name="VAR">text</field>
            </block>
          </value>
        </block>
        <block type="text_changeCase">
          <value name="TEXT">
            <shadow type="text">
              <field name="TEXT">abc</field>
            </shadow>
          </value>
        </block>
        <block type="text_trim">
          <value name="TEXT">
            <shadow type="text">
              <field name="TEXT">abc</field>
            </shadow>
          </value>
        </block>
        <block type="text_count">
          <value name="SUB">
            <shadow type="text"></shadow>
          </value>
          <value name="TEXT">
            <shadow type="text"></shadow>
          </value>
        </block>
        <block type="text_replace">
          <value name="FROM">
            <shadow type="text"></shadow>
          </value>
          <value name="TO">
            <shadow type="text"></shadow>
          </value>
          <value name="TEXT">
            <shadow type="text"></shadow>
          </value>
        </block>
        <block type="text_reverse">
          <value name="TEXT">
            <shadow type="text"></shadow>
          </value>
        </block>
        <label text="Input/Output:" web-class="ioLabel"></label>
        <block type="text_print">
          <value name="TEXT">
            <shadow type="text">
              <field name="TEXT">abc</field>
            </shadow>
          </value>
        </block>
        <block type="text_prompt_ext">
          <value name="TEXT">
            <shadow type="text">
              <field name="TEXT">abc</field>
            </shadow>
          </value>
        </block>
      </category>
      <category name="Lists" categorystyle="list_category">
        <block type="lists_create_with">
          <mutation items="0"></mutation>
        </block>
        <block type="lists_create_with"></block>
        <block type="lists_repeat">
          <value name="NUM">
            <shadow type="math_number">
              <field name="NUM">5</field>
            </shadow>
          </value>
        </block>
        <block type="lists_length"></block>
        <block type="lists_isEmpty"></block>
        <block type="lists_indexOf">
          <value name="VALUE">
            <block type="variables_get">
              <field name="VAR">list</field>
            </block>
          </value>
        </block>
        <block type="lists_getIndex">
          <value name="VALUE">
            <block type="variables_get">
              <field name="VAR">list</field>
            </block>
          </value>
        </block>
        <block type="lists_setIndex">
          <value name="LIST">
            <block type="variables_get">
              <field name="VAR">list</field>
            </block>
          </value>
        </block>
        <block type="lists_getSublist">
          <value name="LIST">
            <block type="variables_get">
              <field name="VAR">list</field>
            </block>
          </value>
        </block>
        <block type="lists_split">
          <value name="DELIM">
            <shadow type="text">
              <field name="TEXT">,</field>
            </shadow>
          </value>
        </block>
        <block type="lists_sort"></block>
        <block type="lists_reverse"></block>
      </category>
      <category name="Colour" categorystyle="colour_category">
        <block type="colour_picker"></block>
        <block type="colour_random"></block>
        <block type="colour_rgb">
          <value name="RED">
            <shadow type="math_number">
              <field name="NUM">100</field>
            </shadow>
          </value>
          <value name="GREEN">
            <shadow type="math_number">
              <field name="NUM">50</field>
            </shadow>
          </value>
          <value name="BLUE">
            <shadow type="math_number">
              <field name="NUM">0</field>
            </shadow>
          </value>
        </block>
        <block type="colour_blend">
          <value name="COLOUR1">
            <shadow type="colour_picker">
              <field name="COLOUR">#ff0000</field>
            </shadow>
          </value>
          <value name="COLOUR2">
            <shadow type="colour_picker">
              <field name="COLOUR">#3333ff</field>
            </shadow>
          </value>
          <value name="RATIO">
            <shadow type="math_number">
              <field name="NUM">0.5</field>
            </shadow>
          </value>
        </block>
      </category>
      <sep></sep>
      <category
        name="Variables"
        categorystyle="variable_category"
        custom="VARIABLE_DYNAMIC"></category>
      <category
        name="Functions"
        categorystyle="procedure_category"
        custom="PROCEDURE"></category>
    </xml>
  </body>
</html>
